// exception.cc 
//	Entry point into the Nachos kernel from user programs.
//	There are two kinds of things that can cause control to
//	transfer back to here from user code:
//
//	syscall -- The user code explicitly requests to call a procedure
//	in the Nachos kernel.  Right now, the only function we support is
//	"Halt".
//
//	exceptions -- The user code does something that the CPU can't handle.
//	For instance, accessing memory that doesn't exist, arithmetic errors,
//	etc.  
//
//	Interrupts (which can also cause control to transfer from user
//	code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "syscall.h"
#include <stdio.h>
#include <iostream>

using namespace std;

int copyin(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes from the current thread's virtual address vaddr.
    // Return the number of bytes so read, or -1 if an error occors.
    // Errors can generally mean a bad virtual address was passed in.
    bool result;
    int n=0;			// The number of bytes copied in
    int *paddr = new int;

    while ( n >= 0 && n < len) {
      result = machine->ReadMem( vaddr, 1, paddr );
      while(!result) // FALL 09 CHANGES
	  {
   			result = machine->ReadMem( vaddr, 1, paddr ); // FALL 09 CHANGES: TO HANDLE PAGE FAULT IN THE ReadMem SYS CALL
	  }	
      
      buf[n++] = *paddr;
     
      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }

    delete paddr;
    return len;
}

int copyout(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes to the current thread's virtual address vaddr.
    // Return the number of bytes so written, or -1 if an error
    // occors.  Errors can generally mean a bad virtual address was
    // passed in.
    bool result;
    int n=0;			// The number of bytes copied in

    while ( n >= 0 && n < len) {
      // Note that we check every byte's address
      result = machine->WriteMem( vaddr, 1, (int)(buf[n++]) );

      if ( !result ) {
	//translation failed
	return -1;
      }

      vaddr++;
    }

    return n;
}

void Create_Syscall(unsigned int vaddr, int len) {
    // Create the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  No
    // way to return errors, though...
    char *buf = new char[len+1];	// Kernel buffer to put the name in

    if (!buf) 
		return;

    if( copyin(vaddr,len,buf) == -1 ) 
	{
		printf("Bad pointer passed to Create\n");
		delete buf;
		return;
    }

    buf[len]='\0';

    fileSystem->Create(buf,0);
    delete[] buf;
    return;
}

int Open_Syscall(unsigned int vaddr, int len) {
    // Open the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  If
    // the file is opened successfully, it is put in the address
    // space's file table and an id returned that can find the file
    // later.  If there are any errors, -1 is returned.
    char *buf = new char[len+1];	// Kernel buffer to put the name in
    OpenFile *f;			// The new open file
    int id;				// The openfile id

    if (!buf) {
	printf("Can't allocate kernel buffer in Open\n");
	return -1;
    }

    if( copyin(vaddr,len,buf) == -1 ) {
	printf("Bad pointer passed to Open\n");
	delete[] buf;
	return -1;
    }

    buf[len]='\0';

    f = fileSystem->Open(buf);
    delete[] buf;

    if ( f ) {
	if ((id = currentThread->space->fileTable.Put(f)) == -1 )
	    delete f;
	return id;
    }
    else
	return -1;
}

void Write_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one. For disk files, the file is looked
    // up in the current address space's open file table and used as
    // the target of the write.
    
    char *buf;		// Kernel buffer for output
    OpenFile *f;	// Open file for output

    if ( id == ConsoleInput) return;
    
    if ( !(buf = new char[len]) ) {
	printf("Error allocating kernel buffer for write!\n");
	return;
    } else {
        if ( copyin(vaddr,len,buf) == -1 ) {
	    printf("Bad pointer passed to to write: data not written\n");
	    delete[] buf;
	    return;
	}
    }

    if ( id == ConsoleOutput) {
      for (int ii=0; ii<len; ii++) {
	printf("%c",buf[ii]);
      }

    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    f->Write(buf, len);
	} else {
	    printf("Bad OpenFileId passed to Write\n");
	    len = -1;
	}
    }

    delete[] buf;
}

int Read_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one.    We reuse len as the number of bytes
    // read, which is an unnessecary savings of space.
    char *buf;		// Kernel buffer for input
    OpenFile *f;	// Open file for output

    if ( id == ConsoleOutput) return -1;
    
    if ( !(buf = new char[len]) ) {
	printf("Error allocating kernel buffer in Read\n");
	return -1;
    }

    if ( id == ConsoleInput) {
      //Reading from the keyboard
      scanf( buf);

      if ( copyout(vaddr, len, buf) == -1 ) {
	printf("Bad pointer passed to Read: data not copied\n");
      }
    } else {
	if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
	    len = f->Read(buf, len);
	    if ( len > 0 ) {
	        //Read something from the file. Put into user's address space
  	        if ( copyout(vaddr, len, buf) == -1 ) {
		    printf("Bad pointer passed to Read: data not copied\n");
		}
	    }
	} else {
	    printf("Bad OpenFileId passed to Read\n");
	    len = -1;
	}
    }

    delete[] buf;
    return len;
}

void Close_Syscall(int fd) {
    // Close the file associated with id fd.  No error reporting.
    OpenFile *f = (OpenFile *) currentThread->space->fileTable.Remove(fd);

    if ( f ) {
      delete f;
    } else {
      printf("Tried to close an unopen file\n");
    }
}

// BEGIN ROHIT'S SYSCALLS

void Print_Syscall(unsigned int vaddr, int len, int arg1, int arg2)
{
	char *buf;		// Kernel buffer for output
    OpenFile *f;	// Open file for output
	bool firstArg = true;
	    
    if ( !(buf = new char[len]) ) 
	{
		printf("Error allocating kernel buffer for write!\n");
		return;
    } 
	else 
	{
        if ( copyin(vaddr,len,buf) == -1 ) 
		{
			printf("Bad pointer passed to to write: data not written\n");
			delete[] buf;
			return;
		}
    }
	
	for (int ii=0; ii<len; ii++) 
	{
		if(buf[ii] == '%') 
		{
			if(firstArg)
				printf("%d", arg1);
			else 
			{
				printf("%d", arg2);
			}
		}
		else if(buf[ii] == '^')
			printf("\n");
		else
			printf("%c",buf[ii]);
	}
	
    delete[] buf;
}

/*********
 * Locks *
 *********/

int CreateLock_Syscall(unsigned int vaddr, int len) 
{
	char *buf = new char[len+1];
	int availableSlot = -1;
	if (!buf) 
		return (-1);
	if ( copyin(vaddr,len,buf) == -1 )
	{
		printf("CreateLock Syscall: Bad pointer passed. Lock not created\n");
		delete[] buf;
		return -1;
	}
	buf[len]='\0';
	
	// Acquire table lock
	kernelLockTableLock->Acquire();
	
	availableSlot = lockMap->Find();
	if(availableSlot != -1)
	{		
		if(buf == NULL)
		{
			printf("CreateLock Syscall: Invalid name. Lock not created\n");
			lockMap->Clear(availableSlot);
			availableSlot = -1;
		}
		else
		{
			OS_LOCKS[availableSlot] = new KernelLock();
			OS_LOCKS[availableSlot]->lock = new Lock(buf);
			OS_LOCKS[availableSlot]->addrSpace = currentThread->space;
			OS_LOCKS[availableSlot]->isToBeDeleted = FALSE;
			OS_LOCKS[availableSlot]->numberOfUsers = 0;
		}
	}
	else
	{
		// cannot create more locks
		printf( "CreateLock Syscall: Cannot create lock.  Maximum number already created.\n");
	}
	
	kernelLockTableLock->Release();
	
	delete[] buf;
	return availableSlot;
}

void DestroyLock_Syscall(int l_index)
{
	// Is the lock within range?
	if(l_index < 0 || l_index >= MAX_LOCKS)
	{
		printf("DestroyLock Syscall: Bad index passed. Lock not destroyed.\n");
	}
	
	kernelLockTableLock->Acquire();
	
	// if lock exists and if it belongs to the same parent process then only try destroying it
	if(OS_LOCKS[l_index] != NULL && OS_LOCKS[l_index]->addrSpace == currentThread->space)
	{
		if(OS_LOCKS[l_index]->numberOfUsers == 0) //if nobody is waiting for lock
		{
			// Delete lock
			lockMap->Clear(l_index);
			delete OS_LOCKS[l_index]->lock;
			delete OS_LOCKS[l_index];
		}
		else
		{
			OS_LOCKS[l_index]->isToBeDeleted = TRUE; //set the flag to delete it in release function
		}
	}
	else
	{
		printf("DestroyLock Syscall: Invalid lock passed.\n");
	}
	
	kernelLockTableLock->Release();
}

/*This acquirelock sys call returns 1 for success and -1 for false*/
void AcquireLock_Syscall(int l_index) 
{	
	// Is the lock within range?
	if(l_index < 0 || l_index >= MAX_LOCKS)
	{
		printf("AcquireLock Syscall: Bad index passed. Lock not acquired.\n");
	}
	
	kernelLockTableLock->Acquire();
	
	// if lock exists and if it belongs to the same parent process then only try acquiring it
	if(OS_LOCKS[l_index] != NULL)
	{
		if (OS_LOCKS[l_index]->addrSpace == currentThread->space &&
			OS_LOCKS[l_index]->isToBeDeleted == FALSE)
		{
			OS_LOCKS[l_index]->numberOfUsers++;
			kernelLockTableLock->Release();
			OS_LOCKS[l_index]->lock->Acquire();
		}
	}
	else
	{
		printf("AcquireLock Syscall: Invalid lock passed.\n");
		kernelLockTableLock->Release();
	}
}

/*This release lock sys call returns 1 for success and -1 for false*/
void ReleaseLock_Syscall( int l_index) 
{
	
	// Is the lock within range?
	if(l_index < 0 || l_index >= MAX_LOCKS)
	{
		printf("ReleaseLock Syscall: Bad index passed. Lock not acquired.\n");
	}
	
	kernelLockTableLock->Acquire();
	
	// if lock exists and if it belongs to the same parent process then only try releasing it
	if(OS_LOCKS[l_index] != NULL)
	{
		if( OS_LOCKS[l_index]->addrSpace == currentThread->space)
		{
			OS_LOCKS[l_index]->lock->Release();
			OS_LOCKS[l_index]->numberOfUsers--;
		
			// If it was scheduled to be deleted and this is the last user, delete it
			if(OS_LOCKS[l_index]->isToBeDeleted && OS_LOCKS[l_index]->numberOfUsers == 0)
			{
				lockMap->Clear(l_index);
				delete OS_LOCKS[l_index]->lock;
				delete OS_LOCKS[l_index];
			}
		}
	}
	else
	{
		printf("ReleaseLock Syscall: Invalid lock passed.\n");
	}
	
	kernelLockTableLock->Release();
}


/***********************
 * Condition Variables *
 ***********************/

int CreateCV_Syscall(unsigned int vaddr, int len) 
{	
	char *buf = new char[len+1];
	int availableSlot = -1;
	if (!buf) return (-1);
	if ( copyin(vaddr,len,buf) == -1 )
	{
		printf("Bad pointer passed to to Create_CV: CV not created\n");
		delete[] buf;
		return -1;
	}
	buf[len]='\0';
	
	// Acquire table lock
	kernelCVTableLock->Acquire();
	
	availableSlot = cvMap->Find();
	if(availableSlot != -1)
	{		
		// validate the CV name and length
		if(buf == NULL || buf <= 0)
		{
			printf("CreateCV Syscall: Invalid name. CV not created\n");
			cvMap->Clear(availableSlot);
			availableSlot = -1;
		}
		else
		{
			OS_CVS[availableSlot] = new KernelCV();
			OS_CVS[availableSlot]->condition = new Condition(buf);
			OS_CVS[availableSlot]->addrSpace = currentThread->space;
			OS_CVS[availableSlot]->isToBeDeleted = FALSE;
			OS_CVS[availableSlot]->numberOfUsers = 0;
		}
	}
	else
	{
		// cannot create more locks
		printf( "CreateCV Syscall: Cannot create CV.  Maximum number already created.\n");
	}
	
	kernelCVTableLock->Release();
	
	delete[] buf;
	return availableSlot;
}

void DestroyCV_Syscall(int cv_index)
{
	// Is the CV within range?
	if(cv_index < 0 || cv_index >= MAX_CVS)
	{
		printf("DestroyCV Syscall: Bad index passed. CV not destroyed.\n");
	}
	
	kernelCVTableLock->Acquire();
	
	// if CV exists and if it belongs to the same parent process then only try destroying it
	if(OS_CVS[cv_index] != NULL && OS_CVS[cv_index]->addrSpace == currentThread->space)
	{
		if(OS_CVS[cv_index]->numberOfUsers == 0) //if nobody is waiting for lock
		{
			// Delete lock
			cvMap->Clear(cv_index);
			delete OS_CVS[cv_index]->condition;
			delete OS_CVS[cv_index];
		}
		else
		{
			OS_CVS[cv_index]->isToBeDeleted = TRUE; //set the flag to delete it in release function
		}
	}
	else
	{
		printf("DestroyCV Syscall: Invalid CV passed.\n");
	}
	
	kernelCVTableLock->Release();
}

void WaitCV_Syscall(int cv_index, int lock_index) 
{	
	// Is the CV index within the possible range?
	if(cv_index < 0 || cv_index >= MAX_CVS)
	{
		printf("Wait Syscall: CV index out of range. Wait failed on CV.\n");
		return;
	}
	
	// Is the Lock index within the possible range?
	if(lock_index < 0 || lock_index >= MAX_LOCKS)
	{
		printf("Wait Syscall: Lock index out of range. Wait failed on CV.\n");
		return;
	}
	
	// Make sure a valid lock was passed (wait will make sure that the thread holds the current lock)
	kernelLockTableLock->Acquire();
	if(OS_LOCKS[lock_index] == NULL ||
	   OS_LOCKS[lock_index]->addrSpace != currentThread->space)
	{
		printf("Wait Syscall: Invalid lock passed.  Wait failed on CV.\n");
		return;
	}
	kernelLockTableLock->Release();
	
	kernelCVTableLock->Acquire();
	
	// If CV exists and if it belongs to the same parent process then only try to wait
	if(OS_CVS[cv_index] != NULL &&
	   OS_CVS[cv_index]->isToBeDeleted == FALSE && 
	   OS_CVS[cv_index]->addrSpace == currentThread->space)
	{
		// Release lock and call wait
		OS_CVS[cv_index]->numberOfUsers++;
		kernelCVTableLock->Release();
		OS_CVS[cv_index]->condition->Wait(OS_LOCKS[lock_index]->lock);
		OS_CVS[cv_index]->numberOfUsers--;
		
		// If it was scheduled to be deleted and this is the last user, delete it
		if(OS_CVS[cv_index]->isToBeDeleted && OS_CVS[cv_index]->numberOfUsers == 0)
		{
			cvMap->Clear(cv_index);
			delete OS_CVS[cv_index]->condition;
			delete OS_CVS[cv_index];
		}
	}
	else
	{
		//cannot wait
		if(OS_CVS[cv_index]->isToBeDeleted == TRUE)
			printf("Wait Syscall: CV is scheduled for deletion. Cannot perform wait.\n");
		
		if(OS_CVS[cv_index]->addrSpace != currentThread->space || OS_CVS[cv_index] == NULL)
			printf("Wait Syscall: Invalid CV passed.  Cannot perform wait.\n");
		kernelCVTableLock->Release();
	}
}

void SignalCV_Syscall(int cv_index, int lock_index) 
{	
	// Is the CV index within the possible range?
	if(cv_index < 0 || cv_index >= MAX_CVS)
	{
		printf("Signal Syscall: CV index out of range. Broadcast failed on CV.\n");
		return;
	}
	
	// Is the Lock index within the possible range?
	if(lock_index < 0 || lock_index >= MAX_LOCKS)
	{
		printf("Signal Syscall: Lock index out of range. Broadcast failed on CV.\n");
		return;
	}
	
	// Make sure a valid lock was passed (broadcast will make sure that the thread holds the current lock)
	kernelLockTableLock->Acquire();
	if(OS_LOCKS[lock_index] == NULL ||
	   OS_LOCKS[lock_index]->addrSpace != currentThread->space)
	{
		printf("Signal Syscall: Invalid lock passed.  Broadcast failed on CV.\n");
		return;
	}
	kernelLockTableLock->Release();
	
	kernelCVTableLock->Acquire();
	
	// if CV exists and if it belongs to the same parent process then only try to signal it
	if(OS_CVS[cv_index] != NULL && 
	   OS_CVS[cv_index]->addrSpace == currentThread->space)
	{
		// execute putting cv to signal
		OS_CVS[cv_index]->condition->Signal(OS_LOCKS[lock_index]->lock);
	}
	else
	{
		//cannot signal cv
		if(OS_CVS[cv_index]->isToBeDeleted == TRUE)
			printf("Signal Syscall: CV is scheduled for deletion. Cannot perform broadcast.\n");
		
		if(OS_CVS[cv_index]->addrSpace != currentThread->space || OS_CVS[cv_index] == NULL)
			printf("Signal Syscall: Invalid CV passed.  Cannot perform broadcast.\n");
	}
	
	kernelCVTableLock->Release();
}

void BroadcastCV_Syscall(int cv_index, int lock_index) 
{		
	// Is the CV index within the possible range?
	if(cv_index < 0 || cv_index >= MAX_CVS)
	{
		printf("Broadcast Syscall: CV index out of range. Broadcast failed on CV.\n");
		return;
	}
	
	// Is the Lock index within the possible range?
	if(lock_index < 0 || lock_index >= MAX_LOCKS)
	{
		printf("Broadcast Syscall: Lock index out of range. Broadcast failed on CV.\n");
		return;
	}
	
	// Make sure a valid lock was passed (broadcast will make sure that the thread holds the current lock)
	kernelLockTableLock->Acquire();
	if(OS_LOCKS[lock_index] == NULL ||
	   OS_LOCKS[lock_index]->addrSpace != currentThread->space)
	{
		printf("Broadcast Syscall: Invalid lock passed.  Broadcast failed on CV.\n");
		return;
	}
	kernelLockTableLock->Release();
	
	kernelCVTableLock->Acquire();
	
	// if CV exists and if it belongs to the same parent process then only try broadcasting it
	if(OS_CVS[cv_index] != NULL && OS_CVS[cv_index]->addrSpace == currentThread->space)
	{
		// execute putting cv to broadcast
		OS_CVS[cv_index]->condition->Broadcast(OS_LOCKS[lock_index]->lock);
		DEBUG('a', "Broadcast Syscall: Broadcast performed\n");
	}
	else
	{
		//cannot broadcast lock
		if(OS_CVS[cv_index]->isToBeDeleted == TRUE)
			printf("Broadcast Syscall: CV is scheduled for deletion. Cannot perform broadcast.\n");
		
		if(OS_CVS[cv_index]->addrSpace != currentThread->space || OS_CVS[cv_index] == NULL)
			printf("Broadcast Syscall: Invalid CV passed.  Cannot perform broadcast.\n");
	}
	
	kernelCVTableLock->Release();
}

/********************
 * Thread & Process *
 ********************/

void kernel_thread(int vaddr)
{
	// Initialize user registers
	currentThread->space->InitRegisters();
	// restore them to machine
	currentThread->space->RestoreState();
	// Write PC
	machine->WriteRegister(PCReg, vaddr);
	// Write next address
	machine->WriteRegister(NextPCReg, vaddr + 4);	// Instructions are 4 bytes.
	machine->WriteRegister(StackReg, currentThread->stackBottom - 16);  // Not sure why we are subtracting 16,
																		//the professor said we should do it.
	// Run it!
	machine->Run();
}

void exec_thread(int vaddr)
{
	// Initialize registers and restore them to machine
	currentThread->space->InitRegisters();
	currentThread->space->RestoreState();
	
	// Run it!
	machine->Run();
}

void Fork_Syscall(unsigned int vaddr)
{
	int virtAddr;
	int stackLoc;
	SpaceId sid;
	Thread* newThread;
	
	virtAddr = machine->ReadRegister(4);
	
	// Get SpaceID
	processTableLock->Acquire();
	for(int i = 0; i < MAX_PROCESSES; i++)
	{
		if(currentThread->space == processTable[i]->space)
		{
			DEBUG('a', "Fork Syscall: SpaceID found %d\n", i);
			sid = i;
			break;
		}
	}
	// Increment child count
	processTable[sid]->numChildren++;
	processTableLock->Release();
	
	// Create thread and set address space
	newThread = new Thread("k_thread");
	newThread->space = currentThread->space;
	
	// Get stack
	stackLoc = newThread->space->CreateThreadStack();
	newThread->stackBottom = stackLoc;
	
	DEBUG('a', "Fork syscall: New stack location: %d\n", stackLoc);
	
	// Fork it
	newThread->Fork(kernel_thread, vaddr);
}

SpaceId Exec_Syscall(int vaddr, int len)
{
	char *buf = new char[len+1];	// Kernel buffer to put the name in
    OpenFile *executable;			// The new open file
	AddrSpace *space;				// New Address space
    SpaceId id;							// The space id
	Thread* newThread;				// New thread for process
	
	// Get name of executable
    if(len == -1)
	{
		buf = (char*)vaddr;
	}
	else
	{
	   if (!buf) 
	   {
		   printf("Can't allocate kernel buffer in Exec\n");
		   return -1;
	   }
	
	   if( copyin(vaddr,len,buf) == -1 ) 
	   {
		   printf("Bad pointer passed to Exec\n");
		   delete[] buf;
		   return -1;
	   }
	
		buf[len]='\0';
	}
	
	// Attempt to open executable
	executable = fileSystem->Open(buf);
	
    if (executable == NULL) 
	{
		printf("Unable to open file %s\n", buf);
		return -1;
    }
	
	// Create new address space and load program into memory
    space = new AddrSpace(executable);
	
	// Create new thread
	newThread = new Thread("exec_thread");
	newThread->space = space;
	
	// Add process to process table
	processTableLock->Acquire();
	id = nextSpaceId++;
	DEBUG('a', "Exec syscall: New process ID: %d\n", id);
	processTable[id] = new ProcessTable;
	processTable[id]->space = space;
	processTable[id]->numChildren++;
	processTable[id]->spaceid = id;
	processTableLock->Release();
	
	// Write SpaceId to register 2
	machine->WriteRegister(2, id);

    delete executable;			// close file	
	
	// Increment active process count
	numberOfProcesses++;
	
	// Fork it
	newThread->Fork(exec_thread, 0);
	
	DEBUG('a', "Exec syscall: Process forked\n");
	
	return id;
}

void Exit_Syscall(int exitCode)
{
	int sid;
	int threadCount;
	
	// Find SpaceID
	processTableLock->Acquire();
	for(int i = 0; i < MAX_PROCESSES; i++)
	{
		if (processTable[i] != NULL) 
		{
			if(currentThread->space == processTable[i]->space)
			{
				DEBUG('a', "Exit syscall: Process id: %d\n", i);
				sid = i;
				threadCount = processTable[i]->numChildren;
				break;
			}
		}
	}
			
	// This is the last thread of the last process
	if(numberOfProcesses == 1 && threadCount == 1)
	{
		DEBUG('a', "Exit syscall: Thread is last thread of last process.  Terminating nachos\n");
		processTableLock->Release();
		interrupt->Halt();
	}
	// This is the last thread of the process, but more processes exist
	else if (numberOfProcesses > 1 && threadCount == 1)
	{
		DEBUG('a', "Exit syscall: Thread is last thread of process. More processes running\n");
		
		// Check locks
		for (int i = 0; i < MAX_LOCKS; i++) 
		{
			if(OS_LOCKS[i] != NULL)
			{
				if(OS_LOCKS[i]->addrSpace == currentThread->space)
					delete OS_LOCKS[i];
			}
		}
		
		// Check CVs
		for(int i = 0; i < MAX_CVS; i++)
		{
			if(OS_CVS[i] != NULL)
			{
				if(OS_CVS[i]->addrSpace == currentThread->space)
					delete OS_CVS[i];
			}
		}
		
		// Deallocate memory
		delete currentThread->space;
		
		// Delete process from process table
		delete processTable[sid];
		
		// Decrement active number of processes
		numberOfProcesses--;
		
		// Terminate thread
		currentThread->Finish();
	}
	// This is not the last thread
	else
	{
		DEBUG('a', "Exit syscall: Thread is not last thread in process\n");
		
		// Decrement thread count and deallocate stack
		processTable[sid]->numChildren--;
		currentThread->space->DeleteThreadStack(currentThread->stackBottom);
		
		processTableLock->Release();
		
		// Terminate thread
		currentThread->Finish();
	}
}

void ExceptionHandler(ExceptionType which) {
    int type = machine->ReadRegister(2); // Which syscall?
    int rv=0; 	// the return value from a syscall

    if ( which == SyscallException ) {
	switch (type) {
	    default:
			DEBUG('a', "Unknown syscall - shutting down.\n");
	    case SC_Halt:
			DEBUG('a', "Shutdown, initiated by user program.\n");
			interrupt->Halt();
			break;
	    case SC_Create:
			DEBUG('a', "Create syscall.\n");
			Create_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
	    case SC_Open:
			DEBUG('a', "Open syscall.\n");
			rv = Open_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
	    case SC_Write:
			DEBUG('a', "Write syscall.\n");
			Write_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
			break;
	    case SC_Read:
			DEBUG('a', "Read syscall.\n");
			rv = Read_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
			break;
	    case SC_Close:
			DEBUG('a', "Close syscall.\n");
			Close_Syscall(machine->ReadRegister(4));
			break;
	    case SC_Yield:
			DEBUG('a', "Yield syscall.\n");
			currentThread->Yield();
			break;
		case SC_CreateLock:
			DEBUG('a', "CreateLock syscall.\n");
			rv = CreateLock_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
		case SC_DestroyLock:
			DEBUG('a', "DestroyLock syscall.\n");
			DestroyLock_Syscall(machine->ReadRegister(4));
			break;
		case SC_AcquireLock:
			DEBUG('a', "AcquireLock syscall.\n"); 
			AcquireLock_Syscall(machine->ReadRegister(4));
			break;
		case SC_ReleaseLock:
			DEBUG('a', "ReleaseLock syscall.\n");
			ReleaseLock_Syscall(machine->ReadRegister(4));
			break;
		case SC_Fork:
			DEBUG('a', "Fork syscall.\n");
			Fork_Syscall(machine->ReadRegister(4));
			break;
		case SC_Print:
			DEBUG('a', "Print syscall.\n");
			Print_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6), machine->ReadRegister(7));
			break;			
		case SC_CreateCV:
			DEBUG('a', "CreateCV syscall.\n");
			rv = CreateCV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
		case SC_DestroyCV:
			DEBUG('a', "DestroyCV syscall.\n");
			DestroyCV_Syscall(machine->ReadRegister(4));
			break;
		case SC_WaitCV:
			DEBUG('a', "WaitCV syscall.\n");
			WaitCV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
		case SC_SignalCV:
			DEBUG('a', "Signal CV syscall.\n");
			SignalCV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
		case SC_BroadcastCV:
			DEBUG('a', "BroadcastCV syscall.\n");
			BroadcastCV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
		case SC_Exec:
			DEBUG('a', "Exec syscall.\n");
			Exec_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
		case SC_Exit:
			DEBUG('a', "Exit syscall.\n");
			Exit_Syscall(machine->ReadRegister(4));
			break;
			
	}

	// Put in the return value and increment the PC
	machine->WriteRegister(2,rv);
	machine->WriteRegister(PrevPCReg,machine->ReadRegister(PCReg));
	machine->WriteRegister(PCReg,machine->ReadRegister(NextPCReg));
	machine->WriteRegister(NextPCReg,machine->ReadRegister(PCReg)+4);
	return;
    } else {
      cout<<"Unexpected user mode exception - which:"<<which<<"  type:"<< type<<endl;
      interrupt->Halt();
    }
}
